﻿using System;
using System.Collections;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Threading;
using System.IO;
using jellybird.core;

namespace jellybird.core
{
	public sealed class AccountManager : IList<TwAccount>, IList, INotifyCollectionChanged
	{
		/* singleton */

		private List<TwAccount> list_internal;

		private object SyncRoot
		{
			get
			{
				
				return ((ICollection)list_internal).SyncRoot;
			}
		}

		#region serialize
		public void Save()
		{
			string file_name=Path.Combine
				(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
				 "jellybird",
				 CRED_FILE_NAME);
			
			byte[] cred_bytes=serialize_internal();
			
			//check dir exists
			string dir = Path.GetDirectoryName(file_name);
			Directory.CreateDirectory(dir);

			File.WriteAllBytes(file_name,cred_bytes);
		}
		
		private byte[] serialize_internal()
		{
			List<TwCredentials> cred_list=new List<TwCredentials>(Count);
			foreach(var item in this)
			{
				cred_list.Add(item.Credentials);
			}
			MemoryStream ms=new MemoryStream();
			StreamWriter writer=new StreamWriter(ms,Encoding.UTF8);
			Helper.SerializeJson(cred_list,writer);
			writer.Flush();
			byte[] ret=ms.ToArray();
			writer.Close();
			return ret;
		}

		private static AccountManager deserialize_internal(byte[] source)
		{
			MemoryStream ms = new MemoryStream(source);
			StreamReader reader = new StreamReader(ms, Encoding.UTF8);
			List<TwCredentials> cred_list = null;
			Helper.DeserializeJson<List<TwCredentials>>(reader, out cred_list);

			AccountManager ret = new AccountManager();
			if(cred_list!=null)
			{
				foreach (var cred in cred_list)
				{
					TwAccount acc = new TwAccount()
					{
						Credentials = cred
					};
					acc.Verify();
					ret.Add(acc);
				}
			}
			
			return ret;
		}
		#endregion
		
		#region Singleton
		private AccountManager()
		{
			list_internal=new List<TwAccount>();
		}

		public static AccountManager Instance
		{
			get
			{
				if(m_instanse==null)
				{
					AccountManager temp_instance=null;
					string file_name=Path.Combine
						(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
						 "jellybird",
						 CRED_FILE_NAME);
					if(File.Exists(file_name))
					{
						byte[] fs_bytes=File.ReadAllBytes(file_name);
						temp_instance=deserialize_internal(fs_bytes);
					}
					if(temp_instance==null)
					{
						temp_instance=new AccountManager();
					}
					System.Threading.Interlocked.CompareExchange(ref m_instanse,temp_instance,null);
				}
				
				return m_instanse;
			}
		}

		
		
		private const string CRED_FILE_NAME="twaccounts.json";
		private static AccountManager m_instanse;

		#endregion

		#region events
		
		
		
		#endregion
		
		#region Члены IList<TwAccount>

		public int IndexOf(TwAccount item)
		{
			int ret = -1;
			lock (SyncRoot)
			{
				ret = list_internal.IndexOf(item);
			}
			return ret;
		}

		public void Insert(int index, TwAccount item)
		{
			lock (SyncRoot)
			{
				list_internal.Insert(index, item);
			}

			On_collection_changed(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
		}

		public void RemoveAt(int index)
		{
			TwAccount removed = null;
			lock (SyncRoot)
			{
				removed = list_internal[index];
				list_internal.RemoveAt(index);
			}

			On_collection_changed(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
		}

		public TwAccount this[int index]
		{
			get
			{
				TwAccount ret = null;
				lock (SyncRoot)
				{
					ret = list_internal[index];
				}
				return ret;
			}
			set
			{
				TwAccount old_item=null;
				TwAccount new_item=value;
				lock (list_internal)
				{
					if(index<list_internal.Count)
						old_item=list_internal[index];
					list_internal[index] = value;
				}

				if (old_item != null)
					On_collection_changed
						(new NotifyCollectionChangedEventArgs
						 (NotifyCollectionChangedAction.Replace,
						  new_item,
						  old_item));
			}
		}

		#endregion

		#region Члены ICollection<TwAccount>

		public void Add(TwAccount item)
		{
			lock (SyncRoot)
			{
				list_internal.Add(item);
			}

			On_collection_changed(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
		}

		public void Clear()
		{
			lock (SyncRoot)
			{
				list_internal.Clear();
			}

			On_collection_changed(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
		}

		public bool Contains(TwAccount item)
		{
			bool ret = false;
			lock (SyncRoot)
			{
				ret = list_internal.Contains(item);
			}
			return ret;
		}

		public void CopyTo(TwAccount[] array, int arrayIndex)
		{
			lock (SyncRoot)
			{
				list_internal.CopyTo(array, arrayIndex);
			}
		}

		public int Count
		{
			get
			{
				int ret = 0;
				lock (SyncRoot)
				{
					ret = list_internal.Count;
				}
				return ret;
			}
		}

		public bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		public bool Remove(TwAccount item)
		{
			bool ret = false;
			lock (SyncRoot)
			{
				ret = list_internal.Remove(item);
			}

			if (ret)
				On_collection_changed
					(new NotifyCollectionChangedEventArgs
					 (NotifyCollectionChangedAction.Remove,
					  item));

			return ret;
		}

		#endregion

		#region Члены IEnumerable<TwAccount>

		public IEnumerator<TwAccount> GetEnumerator()
		{
			IEnumerator<TwAccount> ret = null;
			lock (SyncRoot)
			{
				ret = list_internal.GetEnumerator();
			}
			return ret;
		}

		#endregion

		#region Члены IEnumerable

		IEnumerator IEnumerable.GetEnumerator()
		{
			IEnumerator ret = null;
			lock (SyncRoot)
			{
				ret = ((ICollection)list_internal).GetEnumerator();
			}
			return ret;
		}

		#endregion

		#region Члены IList

		int IList.Add(object value)
		{
			int ret = -1;
			lock(SyncRoot)
			{
				ret = ((IList)list_internal).Add(value);
			}

			On_collection_changed
				(new NotifyCollectionChangedEventArgs
				 (NotifyCollectionChangedAction.Add,
				  value));

			return ret;
		}

		void IList.Clear()
		{
			lock (SyncRoot)
			{
				((IList)list_internal).Clear();
			}

			On_collection_changed
				(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
		}

		bool IList.Contains(object value)
		{
			bool ret = false;
			lock (SyncRoot)
			{
				ret = ((IList)list_internal).Contains(value);
			}
			return ret;
		}

		int IList.IndexOf(object value)
		{
			int ret = -1;
			lock (SyncRoot)
			{
				ret = ((IList)list_internal).IndexOf(value);
			}
			return ret;
		}

		void IList.Insert(int index, object value)
		{
			lock (SyncRoot)
			{
				((IList)list_internal).Insert(index, value);
			}

			On_collection_changed
				(new NotifyCollectionChangedEventArgs
				 (NotifyCollectionChangedAction.Add,
				  value,
				  index));
		}

		bool IList.IsFixedSize
		{
			get { return false; }
		}

		bool IList.IsReadOnly
		{
			get { return false; }
		}

		void IList.Remove(object value)
		{
			lock (SyncRoot)
			{
				((IList)list_internal).Remove(value);
			}

			On_collection_changed
				(new NotifyCollectionChangedEventArgs
				 (NotifyCollectionChangedAction.Remove,
				  value));
		}

		void IList.RemoveAt(int index)
		{
			TwAccount removed = null;
			lock (SyncRoot)
			{
				removed = this[index];
				((IList)list_internal).RemoveAt(index);
			}

			if (removed != null)
			{
				On_collection_changed
					(new NotifyCollectionChangedEventArgs
					 (NotifyCollectionChangedAction.Remove,
					  removed));
			}
		}

		object IList.this[int index]
		{
			get
			{
				return this[index];
			}
			set
			{
				TwAccount old_item = null;
				TwAccount new_item = (TwAccount)value;
				lock (SyncRoot)
				{
					if (index < list_internal.Count)
						old_item = this[index];
					((IList)list_internal)[index] = value;
				}

				if (old_item != null)
					On_collection_changed
						(new NotifyCollectionChangedEventArgs
						 (NotifyCollectionChangedAction.Replace,
						  new_item,
						  old_item));
			}
		}

		#endregion

		#region Члены ICollection

		void ICollection.CopyTo(Array array, int index)
		{
			lock (SyncRoot)
			{
				((ICollection)list_internal).CopyTo(array, index);
			}
		}

		int ICollection.Count
		{
			get { return Count; }
		}

		bool ICollection.IsSynchronized
		{
			get { return true; }
		}

		object ICollection.SyncRoot
		{
			get { return SyncRoot; }
		}

		#endregion

		#region Члены INotifyCollectionChanged

		private void On_collection_changed(NotifyCollectionChangedEventArgs args)
		{
			if (CollectionChanged != null)
			{
				Dispatcher dispatcher = System.Windows.Application.Current.Dispatcher;
				if (dispatcher.CheckAccess())
					CollectionChanged(this, args);
				else
				{
					Delegate[] inv_list = CollectionChanged.GetInvocationList();
					foreach (var inv in inv_list)
					{
						dispatcher.BeginInvoke(inv, this, args);
					}
				}
			}
		}

		public event NotifyCollectionChangedEventHandler CollectionChanged;

		#endregion
	}
}
